AppTitle "Celestial Rift - Version 1.1 (C)2001 Myke P" ;what this program will be called in MICROSOFT WINDOWS.
;This makes two CONSTants. These are values which will NOT change at any point in the program, so we set their values now.
Const SCREEN_WIDTH = 800
Const SCREEN_HEIGHT = 600
;This is the code which tells Blitz what Display Resolution to use on the Graphics Card. The "GRAPHICS" command should always be placed before you do
;*anything* image-related in your program
Graphics SCREEN_WIDTH,SCREEN_HEIGHT,0,1 ;start the graphics mode at SCREEN_WIDTH by SCREEN_HEIGHT, let Blitz choose the depth (,0) and run full screen (,1)
;more program CONSTants..
Const GAME_AREA_X = 50000 ;these two set the size of the map. You *should* keep them the same, 'cos the radar is square
Const GAME_AREA_Y = 50000 ;but technically, you can change the values to anything you like! ;)
;the following constants are keyboard "SCAN" codes. Every key on the keyboard has a number. You can get the full list in your Blitz manual.
Const KEY_CLOCKWISE = 25 ;(p)
Const KEY_ANTICWISE = 24 ;(o)
Const KEY_SPEEDUP = 16 ;(q)
Const KEY_SPEEDDOWN = 30 ;(a)
Const KEY_FIRE = 57 ;(Space)
Const KEY_HYPER = 2 ;(1)
Const KEY_BOOST = 50 ;(m)
Const KEY_CLOAK = 46 ;(c)
Const KEY_QUIT = 1 ;(Escape)
Const KEY_PAUSE = 7 ;(Number 6 on the main keyboard)
Const KEY_DEBUG = 59 ;(F1)
Const KEY_SAVESCREEN = 88 ;(F12)
;the following constants affect the way the game plays. Feel free to mess with the values..
Const INCR_ROTATE# = 5 ;.. but DON'T touch this, otherwise the game will crash (I only drew the animation frames for 5 degree intervals!)
Const INCR_SPEED# = 0.5
Const INCR_SLOW# = 0.125
Const INCR_BOOSTERS_UP# = 0.025
Const INCR_BOOSTERS_DOWN# = .75
Const INCR_CLOAK_UP#= 0.0125
Const INCR_CLOAK_DOWN# = .25
Const SPEED_MAX = 25
Const SPEED_MIN = -5
;set up changable variables for game/menu (with initial values, if you like - i.e.: you could just as soon as set them later!)
Global FLAG_GAMEON
Global FLAG_MENUON
Global FLAG_PAUSE
Global FLAG_SAVESCREEN = 0
Global FLAG_DEBUG = 0
Global FLAG_GAMESTARTER
Global PLAYER_SHIELD#
Global PLAYER_BOOSTERS#
Global PLAYER_CLOAK#
Global PLAYER_JUMPS
Global PLAYER_SPEED#
Global PLAYER_ANGLE#
Global PLAYER_X#
Global PLAYER_Y#
Global PLAYER_SCORE
Global HI_SCORE
Global timer
Global frames
Global starson
Global menu_frame
Global menu_accept_quit
Global game_pause_frame
Global game_accept_pause
Global game_pause_stat
Global hypercount# ;a "#" symbol after the variable name means it can hold a FLOATING POINT number, i.e.: 190.1234
Global pausecount ;without the "#" symbol, the variable is, by default, an INTEGER (Whole) number, i.e.: 190
Global cloakon# ;use the symbols when you are *sure* that you want it to hold specific types of data:
Global frame1 ;# - floating point
Global frame2 ;% - integer (whole number)
Global frame3 ;$ - string (text, i.e.: "MYKE 12345"
Global frame4 ;
Global frame5
Global frame6
Global frame7
Global frame8
Global tempstr$
timer = CreateTimer(50) ;create a timer set at 50ms (game speed) - play with this to see how you can increase or decrease the speed of the game.
;this should be set at a speed which will look near enough the same on *every* PC it will be played on.
;My PC (a 733MHz PIII with an nVidia GeForce card) will handle upwards of 150 frames per second, quite happily
;but 'lesser' machines will not. 50, therefore, is quite sensible For a game of this nature, who's minimum system spec
;will be something like a PII 300MHz machine (i.e.: Blitz Basic's minimum spec!)
;NOTE: there's no need to organise your variable declarations, as I have here, into sections. They can appear in any order you like, before the main program begins.
;I just do this, 'cos it looks right professional! :))))
;picture/animation (and related) variables
Dim game_stars(5) ;these 3 are ARRAYS. An array is automatically Global, but requires the Keyword (Yellow bit) DIM instead. This means "Dimension".
Dim game_icons(4) ;the arrays have single dimensions (imagine one straight line of boxes, each that can contain a single variable) 0 to.. (the number in brackets)
Global game_icon_dot
Global game_player
Global game_player_frame
Global game_player_dot
Global game_bullet_player
Global game_gameover
Global game_paused
Global menu_hiscore
Global menu_lastscore
Global menu_scorefont
Global menu_start
Global menu_start_stat
Global menu_logo
Global menu_credit
Global menu_guildhall
Global menu_thanks
Global menu_quit
Global menu_ship1
Global menu_ship2
Global menu_ship_hor
Global menu_ship_ver#
Global menu_ship_type
;Types are like Structures in C. You have a "Type" called whatever. Then you can make multiple versions of the type. Each version of the Type has the same properties, i.e.:
;a FISH (the Type) has EYES, MOUTH, SCALES And FINS (it's properties) - ALL FISH have these properties.
;a DOG (the Type) has EYES, MOUTH, FUR and TAIL (it's properties) - ALL DOGS have these properties (look like this.)
Type stars ;create "Type" for parallex stars
Field depth,x#,y# ;each star has a depth, x and y position
End Type
Type icons ;same for icons
Field x#,y#,style,frame#
End Type
;this is for the bullets. I've used an array, rather than a type (which would have been just as good in this case) to show you how MULTIDIMENSIONAL arrays can work.
Const bulletnum = 500
Global bulletlimiter
Global nextbullet = 0
Dim bullets(bulletnum-1,7) ;create an array for 'bulletnum' (0 to bulletnum -1) on-screen bullets with 8 values per bullet:
;we'll say that a bullet's style can also say whether or not it's in use, i.e.: 0 = off, 1 = player, 2 = enemy1
;this code makes 4 'objects' in a "Type" called "ICONS".
For i = 1 To 4 ;do this 1, 2, 3, 4 times
icon.icons = New icons ;create a new icon
icon\style = i ;the icon style for each new icon is equal to the increment of i (i.e.: 1, 2, 3 or 4!)
icon\frame = Int(Rnd(0,5)) ;create a random frame number between 0 and 5 for each new icon
Next
menustars = SCREEN_HEIGHT/3 ;generate a number of stars, so that they look dense enough on all test resolutions
starson = 1 ;tells the program to show the stars (see later)
For i=0 To menustars ;create <menustars> number of stars in the STARS type
star.stars=New stars ;add a new star for each increment of i
Next
;this code loads the image numbers into an array called "game_ stars", which we DIMmed earlier. It has 6 containers (0,1,2,3,4,5) but I'm only using 1 to 5!
;an "image number" is what Blitz uses to reference graphics held in the Video Memory, i.e.:
;1. Image number 12 is a picture of a flower.
;2. Make a variable called "flower_pic" = 12
;3. Wherever Blitz is told to draw "flower_pic", reference image number 12 in the Video Memory.
game_stars(1) = LoadImage("GfxRes/backg-star-1.bmp") ;container (1) in "game_stars" holds the image number for this picture ("GfxRes/backg-star-1.bmp")
MaskImage game_stars(i),255,0,255 ;mask the images for each star, so that MAGENTA (255,0,255) is the transparent colour
Next
;similarly, this code loads the image numbers into an array called "game_icons"
;however, these are images that contain the frames of an Animation, so a different Load command is used.
game_icons(1) = LoadAnimImage("GfxRes/icon-boost.bmp",32,32,0,6) ;LoadAnimImage has the same structure as LoadImage, with additional numbers after the picture filename
game_icons(3) = LoadAnimImage("GfxRes/icon-cloak.bmp",32,32,0,6) ;and the Number of Frames in the Image (as *you* would count them (in this case 6)
game_icons(4) = LoadAnimImage("GfxRes/icon-jump.bmp",32,32,0,6) ;have a look at the file "icon-boost" in Paint Shop Pro and see for yourself the 6 frames of animation.
For i = 1 To 4
MaskImage game_icons(i),255,0,255 ;mask the images for each icon, so that MAGENTA (255,0,255) is the transparent colour
Next
;notice in the following code, we're loading (and MASKING) graphics in exactly the same way as before, but into regular variables instead of arrays.
;this next bit is all animations
game_player = LoadAnimImage("GfxRes/player-ship.bmp",80,80,0,72) ;load in the 'sprite' for the player ship
MaskImage game_player,255,0,255 ;mask the images for the player ship, so that MAGENTA (255,0,255) is the transparent colour
game_bullet_player = LoadAnimImage("GfxRes/bullet-player.bmp",10,10,0,6) ; the blue player bullet animation
MaskImage game_bullet_player,255,0,255 ;mask the images
;these are all plain single-frame pictures
;menu piccies
menu_logo = LoadImage("GfxRes/menu-logo.bmp") ;the CELESTIAL RIFT logo
menu_ship1 = LoadImage("GfxRes/menu-ship1.bmp") ;the big player-type ship
menu_ship2 = LoadImage("GfxRes/menu-ship2.bmp") ;the big enemy type ship
menu_guildhall = LoadImage("GfxRes/menu-guildhall.bmp") ;the GUILDHALL message at the top
menu_thanks = LoadImage("GfxRes/menu-thanks.bmp") ;the THANKS message at the bottom
menu_credit = LoadImage("GfxRes/menu-credit.bmp") ;the CREDITS message below the logo
menu_start = LoadImage("GfxRes/menu-start.bmp") ;the "PRESS FIRE TO START" caption
menu_quit = LoadImage("GfxRes/menu-quit.bmp") ;the "(ESCAPE TO QUIT)" caption
;game piccies
game_gameover = LoadImage("GfxRes/game-gameover.bmp") ;the game over logo, which I tend to see a lot of.. :(
game_paused = LoadImage("GfxRes/game-paused.bmp") ;the paused logo
game_player_dot = LoadImage("GfxRes/player-radardot.bmp") ;radar dots for the player and bonus icons respectively
MaskImage menu_ship1,255,0,255 ;mask all the images.
MaskImage menu_ship2,255,0,255 ;notice that it doesn't matter what order you do the masks in!
MaskImage menu_credit,255,0,255
MaskImage menu_guildhall,255,0,255 ;by the way, if when testing your program, you get the error message "IMAGE DOES NOT EXIST" at this point
MaskImage menu_thanks,255,0,255 ;in the program, it's because the LOADIMAGE command above didn't find the file properly.
MaskImage menu_start,255,0,255 ;This is usually either because; 1 - You've typed the wrong filename in the LOADIMAGE bit, or
MaskImage menu_quit,255,0,255 ;2 - You've got the variable name wrong in the MASKIMAGE bit.
MaskImage game_gameover,255,0,255
MaskImage game_paused,255,0,255
;Right! That's it, we've set up *everything* we're going to need from outside the program.
menu_loop() ;start the proper program loop by 'calling' the function called "menu_loop()" - which, conveniently, is just coming up!!!
;this function keeps the menu loop going.. It starts playing tunes and sets a couple of variables.
;Then it goes into a never ending loop which carries out a sequence of checks and function calls.
Function menu_loop() ;Start of the Function whose name is "menu_loop()"
FLAG_MENUON = 1 ;tells the program that the menu is running, for use in the REPEAT..FOREVER statement in a sec..
menu_ship_hor = (SCREEN_WIDTH/2)-(ImageWidth(menu_ship1)/2) ;this sets the starting horizontal position of the big ship on the menu
menu_ship_ver = SCREEN_HEIGHT+1 ;this sets the vertical starting position of the big ship on the menu at one pixel off the bottom of the screen.
;notice that images are (by default) handled from the TOP-LEFT pixel, so, horizontally, I offset the value
;to the left, by half the width of the ship image.
menu_ship_type = 1 ;set up some initial values for these counters
menu_frame = 1
menu_start_stat = 0
menu_accept_quit = 0
Repeat ;a REPEAT.. FOREVER loop will carry out the code in between for as long as the program is running..
If FLAG_MENUON = 1 Then ;If the FLAG_MENUON flag is set to 1 then "do" the following..
menu_loop_update() ;call function "menu_loop_update()"
;this IF statement checks to see if the game music is playing (as it will be when you quit the game)
Else ;if the FLAG_MENUON flag wasn't set to 1 then "do" the following..
End ;END the program
End If
Forever ;end of the REPEAT.. FOREVER loop
End Function ;End of the function
;"menu_loop_update()" checks for key-presses, alters position coordinates on the screen
;and various variables/flags for use in "menu_draw_update()" and the game functions too.
;in fact, you'll see that this has the same organisational structure as the proper game loop (albeit much less complex!)
Function menu_loop_update()
frames = WaitTimer(timer) ;returns a value to "frames" for how many video screen refreshes (The MHz of your monitor) occured since the last call to our timer
For i = 1 To frames ;update the screen positions for "frames" number of changes.. This enables drawing frames to be skipped on slower machines.
If KeyDown(KEY_QUIT) ;If the user presses the "QUIT" button on the keyboard, then..
FlushKeys ;(when using SCAN CODES, flush the keyboard buffer after each successful "Have they pressed a particular key" question.)
If menu_accept_quit = 1 Then ;If the variable "menu_accept_quit" is 1, then set the FLAG_MENUON to 0.
FLAG_MENUON = 0 ;back in the "menu_loop()" function, this will cause the program to end!
End If
End If
If KeyDown(KEY_FIRE) ;If the user presses the "FIRE" button on the keyboard, then..
FlushKeys
FLAG_DEBUG = 0
FLAG_GAMESTARTER = 1
hypercount = 102
FLAG_PAUSE = 0
game_loop() ;call the function called "game_loop()" (the game will run, but once it has ended, we'll find ourselves back here...)
menu_ship_ver = SCREEN_HEIGHT+1
menu_frame = 1
menu_start_stat = 0
menu_accept_quit = 0 ;by setting this to 0, we'll stop the program ending by any surplus "QUIT" keystrokes carrying over from the game, by
;using the menu_frame counter at the end of this FOR loop.
FLAG_PAUSE = 0
FlushKeys
End If
If KeyDown(KEY_DEBUG) ;if the user presses the "DEBUG" button on the keyboard, then..
FlushKeys
FLAG_DEBUG = 1 ;this is all exactly the same as "FIRE" above, but now FLAG_DEBUG is set to 1.
FLAG_GAMESTARTER = 1 ;later on, you'll see that we use this to turn on, or off, some overlayed displays in the game loop!
hypercount = 102
FLAG_PAUSE = 0
game_loop()
menu_ship_ver = SCREEN_HEIGHT+1
menu_frame = 1
menu_start_stat = 0
menu_accept_quit = 0
FLAG_PAUSE = 0
FlushKeys
End If
If KeyDown(KEY_SAVESCREEN) ;if the user presses the "SAVESCREEN" button on the keyboard, then..
FlushKeys
FLAG_SAVESCREEN = 1 ;just sets FLAG_SAVESCREEN to 1. You'll see this used in the next function.
End If
If menu_ship_ver > (-50 - ImageHeight(menu_ship1)) ;if the bottom of the (largest) ship picture gets to 50 pixels above the top of the screen
Select menu_ship_type ;A SELECT..CASE statement is like an IF statement, but only performs 1 check, i.e.: "What is the value of I?"
Case 1 ;if "menu_ship_type" is 1 then..
menu_ship_ver = menu_ship_ver - .3 ;how fast menuship1 moves up the screen
Case 2 ;if "menu_ship_type" is 1 then..
menu_ship_ver = menu_ship_ver - .8 ;how fast menuship2 moves up the screen
End Select
Else
menu_ship_ver = SCREEN_HEIGHT+1 ;reset the vertical position so that the top of the ship is at the bottom of the screen
menu_ship_hor = Rnd(0,SCREEN_WIDTH-ImageWidth(menu_ship1)) ;create a random horizontal position
menu_ship_type = Int(Rnd(1,2)) ;randomly choose between values 1 or 2
End If
menu_frame = menu_frame + 1 ;increase the value of "menu_frame" by 1
If menu_frame = 25 Then ;if the value of "menu_frame" gets to 25 then reset it to 1
menu_frame = 1
menu_accept_quit = 1 ;now we've turned the user's ability to press the "QUIT" button back on (see the IF statement in the KeyDown(KEY_QUIT) statement above!
If menu_start_stat = 1 Then ;this bit just switches the value of "menu_start_stat" between 1 and 0, i.e. :If it's 1, then make it 0, if it's 0, then make it 1 etc.
menu_start_stat = 0 ;this will be used in the next function to flash the "PRESS FIRE TO START" caption on and off!
Else
menu_start_stat = 1
End If
End If
Next
menu_draw_update() ;call the function "menu_draw_update" which draws all these new things on the screen
End Function
;"menu_draw_update()" just draws things on your monitor, using values set and altered in the last function (menu_loop_update).
Function menu_draw_update()
SetBuffer BackBuffer() ;draw all of the following to the backbuffer() which is an area video memory which is not shown on screen.
;the idea is that you draw everything here, then SHOW it to the user when the full picture is complete.
ClsColor 0,0,0 ;changes the CLearScreen colour to black (0,0,0)
;Color commands use the 3 parameters as RED value, GREEN value, BLUE value
;which is how pixel colours are made up on the monitor. If you're familiar with
;any PC Paint Packages, you'll probably be quite familiar with this.
Cls ;CLears the Screen
;this next section draws all of the pictures on to the backbuffer() at the specified coordinates.
;notice that the pictures are drawn in sequence, with the backmost things drawn first and the foremost things drawn last
;kind of like making a collage!
DrawImage menu_logo,(SCREEN_WIDTH/2)-(ImageWidth(menu_logo)/2),0 ;this draws the menu_logo image at "x","y"
;I've used EQUATIONS to make up x and y for all the images in this project, so that when you change
;the resolution values (back at the beginning of the code) everything still appears at the correct position
;on the screen. If you know what resolution you'll be working at, then you can quite happily put x and y as
;ordinary numbers in here.
;For example, at 800*600, the menu_logo image will be drawn at:
;SCREEN_WIDTH = 800
;800/2 = 400
;ImageWidth(menu_logo) is the width (in pixels) of this particular image (which is 547 pixels)
;547/2 = 273.5
;(800 - 273.5 = 126.5)
;So, menu_logo would be drawn at 127,0 (because Blitz requires whole number coordinates, it will automatically
If PLAYER_X < 0 Then ;if the PLAYER_X value is less than 0, then wrap your position around the map
PLAYER_X = (PLAYER_X + GAME_AREA_X) ;by adding the GAME_AREA_X value to the negative value, i.e.: -5 becomes 19995 on a 20000 X pixel map.
End If
If PLAYER_X > GAME_AREA_X Then ;the same in reverse, i.e. 20004 becomes 4 on the same map.
PLAYER_X = (PLAYER_X - GAME_AREA_X)
End If
If PLAYER_Y < 0 Then ;and now the same for the Y direction
PLAYER_Y = (PLAYER_Y + GAME_AREA_Y)
End If
If PLAYER_Y > GAME_AREA_Y Then
PLAYER_Y = (PLAYER_Y - GAME_AREA_Y)
End If
For icon.icons=Each icons ;the "icons" don't move from their randomly set x,y position, so all we need to do for them is
icon\frame = icon\frame+.5 ;update their animation frame number. There are 6 frames, and we're incrementing at 0.5 frames per update.
If Int(icon\frame) = 6 Then ;in real terms, this means we'll be updating the frame number every 2 updates.
icon\frame = 0 ;Because the icon animations are simple 0 to 5 cycling animations, when the frame number reaches 6, we flip it back to 0
End If ;to start the sequence again.
Next
;this next bit of code creates the "simple but effective" Hyperjump event, based entirely on the value of "hypercount" being set to 1, earlier in the code.
If hypercount > 0 Then
If (hypercount/2 = Int(hypercount/2)) Then ;this line says "If the value of 'hypercount' is an EVEN number"
;because 5 (an odd number) would be 2.5 when devided by 2, but conversion to an INTeger gives 3.
;2.5 does NOT equal 3!
;all of this simply rotates the HyperJump ships in different offset directions
;the only difference is, we're not bothering with degrees here, just animation frame numbers, i.e.: 355 degrees is frame 71 (i.e.: 355/5 degree increment)
frame1 = frame1 + 3
frame2 = frame2 - 3
frame3 = frame1 + 9
frame4 = frame2 - 9
frame5 = frame1 + 36
frame6 = frame2 - 36
frame7 = frame1 + 54
frame8 = frame2 - 54
If frame1 >=72 Then
frame1 = frame1-72
End If
If frame1 < 0 Then
frame1 = frame1+72
End If
If frame2 >=72 Then
frame2 = frame2-72
End If
If frame2 < 0 Then
frame2 = frame2+72
End If
If frame3 >=72 Then
frame3 = frame3-72
End If
If frame3 < 0 Then
frame3 = frame3+72
End If
If frame4 >=72 Then
frame4 = frame4-72
End If
If frame4 < 0 Then
frame4 = frame4+72
End If
If frame5 >=72 Then
frame5 = frame5-72
End If
If frame5 < 0 Then
frame5 = frame5+72
End If
If frame6 >=72 Then
frame6 = frame6-72
End If
If frame6 < 0 Then
frame6 = frame6+72
End If
If frame7 >=72 Then
frame7 = frame7-72
End If
If frame7 < 0 Then
frame7 = frame7+72
End If
If frame8 >=72 Then
frame8 = frame8-72
End If
If frame8 < 0 Then
frame8 = frame8+72
End If
End If ;end of the "if hypercount is EVEN" IF statement
;this bit increases the value of "hypercount" until it reaches 200
hypercount = hypercount + 1
If hypercount > 0 And hypercount <= 102 Then ;between 0 and 102, the player speed increases
PLAYER_SPEED = PLAYER_SPEED + 2
ElseIf hypercount > 102 And hypercount < 200 Then ;between 102 and 200, the player speed decreases (but only if FLAG_GAMESTARTER is 0)
If FLAG_GAMESTARTER = 0 Then ;this is because when the game starts, the player speed is 0, but we've faked being in
PLAYER_SPEED = PLAYER_SPEED - 2 ;the middle of a hyperjump. Without this FLAG, the player ship would start each game
End If ;travelling at about -90 speed backwards, which would just be weird! :)
End If
If hypercount = 100 Then
game_stars_randomize() ;half way through the Hyperjump sequence, the "game_stars_randomize()" function is called, which changes all the star positions
;and depths, to give the impression we've moved to a completely different part of space
End If
If hypercount/10 = Int(hypercount/10) Then
game_player_randomize() ;every 10 updates, the function "game_player_randomize()" is called. This randomly jumps the player around the map
;and confuses the hell out of the bad guys! (well it will by next month's tutorial!!) ;))
End If
If hypercount = 200 Then ;if the "hypercount" variable reaches 200, then stop the sequence and reset the value back to 0
hypercount = 0
FLAG_GAMESTARTER = 0 ;also, when the game first starts we were in the middle of a hyperjump. Resetting this FLAG back to 0 means that
;the next hyperjump sequence will decrease the speed after "hypercount" gets to 103
End If
Else ;if we're not in the middle of a hyperjump event, then:
frame1=game_player_frame-1 ;this bit is just for the DEBUG mode. It was designed to check that 360 degrees became 0 degrees and vice versa correctly
frame2=game_player_frame+1
If frame1 < 0 Then
frame1 = frame1 + 72
End If
If frame2 >= 72 Then
frame2 = frame2 - 72
End If
End If ;end of the "Are we in the middle of a Hyperjump" IF statement
;the parralex stars move relative to the player and is just the same "menustars" number of stars
;scrolled at various speeds, wrapping around the screen.
;it's a fairly cheap, but effective way of creating a nice illusion of speed!
If starson=1 Then ;if, at the beginning of the program, you set "starson" to 0, the stars will disappear.
;the game would also feel pretty bloody wierd.. hold on.. yep.. Absolutely mad! Try it! :)
For star.stars=Each stars
;the following two lines move each version of the "stars" Type an x and y distance relative to the player's speed and angle
;with a devision relative to the depth of the star to make the smallest stars move slower than the biggest
;thus we have our parallex effect.
;it might be of interest to know that these two lines were based on the OLDSKOOL demo which comes with Blitz Basic
;and was the starting point for the whole CELESTIAL RIFT game concept! Thanks a lot, Mr Mikkel L°kke!! :)
;the maximum pixel width of for the biggest star image is 5 pixels
;the following IF statements wrap the stars around the screen border when they reach the extremities
If star\x < -5 Then
star\x = star\x + (SCREEN_WIDTH + 5)
End If
If star\x > SCREEN_WIDTH Then
star\x = star\x - (SCREEN_WIDTH + 5)
End If
If star\y <= -5 Then
star\y = star\y + (SCREEN_HEIGHT + 5)
End If
If star\y >= SCREEN_HEIGHT
star\y = star\y - (SCREEN_HEIGHT + 5)
End If
Next
End If ;end of the "are the stars going to be shown" IF statement
;the following lines calculate all the bullet positions, relative to their originally set SPEED and ANGLE from the "createbullet" function.
For i = 0 To bulletnum-1
If bullets(i,4) > 0 Then ;if the bullet isn't 0, i.e.: it's "on"
bullets(i,0) = bullets(i,0) + (bullets(i,3)*(Sin(bullets(i,2))/2)) ;bullet x position = bullet x position + (bullet speed * (SIN (bullet angle) / 2)
bullets(i,1) = bullets(i,1) - (bullets(i,3)*(Cos(bullets(i,2))/2)) ;bullet y position = bullet y position + (bullet speed * (COS (bullet angle) / 2)
;try turning these last two lines off for a crazy pulsing effect of bullets that you can use
;to surround the player, creating a kind of enemy-deadly minefield.. Madness! :)
bullets(i,5) = bullets(i,5) + 1 ;increases the animation frame of the bullet
If bullets(i,5) = 6 Then ;if the animation frame is 6 then flip it back to 0 (the bullet animation runs frames 0, 1, 2, 3, 4, 5, 0, 1, 2 ... etc)
bullets(i,5) = 0
End If
;as with the player and enemies, this next bit wraps the bullets around the map extremities
If bullets(i,0) < 0 Then
bullets(i,0) = (bullets(i,0) + GAME_AREA_X)
End If
If bullets(i,0) > GAME_AREA_X Then
bullets(i,0) = (bullets(i,0) - GAME_AREA_X)
End If
If bullets(i,1) < 0 Then
bullets(i,1) = (bullets(i,1) + GAME_AREA_X)
End If
If bullets(i,1) > GAME_AREA_Y Then
bullets(i,1) = (bullets(i,1) - GAME_AREA_Y)
End If
;like the enemies "distance from player", this bit checks if a bullet has gone a certain distance from it's origin
temp_x1# = bullets(i,0) - bullets(i,6) ;origin x on the right
temp_y1# = bullets(i,1) - bullets(i,7) ;origin y on the top
temp_x2# = bullets(i,6) - bullets(i,0) ;origin x on the left
temp_y2# = bullets(i,7) - bullets(i,1) ;origin y on the bottom
;wraps distances around the map extremities
If temp_x1# < 0 Then
temp_x1# = temp_x1# + GAME_AREA_X
End If
If temp_x1# > GAME_AREA_X Then
temp_x1# = temp_x1# - GAME_AREA_X
End If
If temp_y1# < 0 Then
temp_y1# = temp_y1# + GAME_AREA_Y
End If
If temp_x1# > GAME_AREA_X Then
temp_y1# = temp_y1# - GAME_AREA_Y
End If
If temp_x2# < 0 Then
temp_x2# = temp_x2# + GAME_AREA_X
End If
If temp_x2# > GAME_AREA_X Then
temp_x2# = temp_x2# - GAME_AREA_X
End If
If temp_y2# < 0 Then
temp_y2# = temp_y2# + GAME_AREA_Y
End If
If temp_x2# > GAME_AREA_X Then
temp_y2# = temp_y2# - GAME_AREA_Y
End If
;calculates the shortest distance
If Abs(temp_x1#) < Abs(temp_x2#) Then
temp_x# = Abs(temp_x1#)
Else
temp_x# = Abs(temp_x2#)
End If
If Abs(temp_y1#) < Abs(temp_y2#) Then
temp_y# = Abs(temp_y1#)
Else
temp_y# = Abs(temp_y2#)
End If
;back to PYTHAGORAS to work out the "As the crow flies" distance from the bullet's original starting point
temp_x# = Abs(temp_x# / 200)
temp_y# = Abs(temp_y# / 200)
temp_hyp# = (temp_x#*temp_x#) + (temp_y#*temp_y#)
temp_hyp# = Sqr(temp_hyp#)
;if the bullet has travelled "5" units from it's starting point, then..
If temp_hyp# > 5 Then
bullets(i,4) = 0 ;turn the bullet off
End If
End If
Next ;end of bullet coordinate repositioning loop
;this is the player's bullet limiting code, basically only allowing the player to fire every 10 frames (otherwise it looks more like the player is spraying water!!)
If bulletlimiter > 0 Then
bulletlimiter = bulletlimiter + 1
If bulletlimiter = 10 Then ;value essentially sets the fire-rate (the lower the number, the faster the fire rate!)
bulletlimiter = 0
End If
End If
PLAYER_SCORE = PLAYER_SCORE + 1 ;if the game is in progress, then increase the PLAYER_SCORE by 1 every screen update
;this acts as a kind of survival bonus for the more defensive player
If PLAYER_SCORE >= HI_SCORE Then
HI_SCORE = PLAYER_SCORE
End If
End If ;end of "IF FLAG_PAUSE = 0" IF Statement
;this flashes the "PAUSED" caption on and off when needs be!
game_pause_frame = game_pause_frame + 1
If game_pause_frame = 25 Then
game_pause_frame = 1
game_accept_pause = 1
If game_pause_stat = 1 Then
game_pause_stat = 0
Else
game_pause_stat = 1
End If
End If
Next ;end of the "for i = 1 to frames" FOR loop
game_draw_update() ;finally, draw all the pictures on the screen, based on their (possibly) new positions.
End Function
;this function draws the game graphics in their freshly calculated positions
Function game_draw_update()
SetBuffer BackBuffer() ;draw all of the following to the backbuffer
ClsColor 0,0,0 ;changes the CLearScreen colour to black (0,0,0)
Cls
If starson=1 Then ;draw all of the stars using their correct pictures, at the correct x and y positions
For star.stars=Each stars ;(for each star in type 'stars' do the following..)
DrawImage game_stars(star\depth),star\x,star\y ;using the depth property of "stars" as the Array position
Next
End If
;this FOR Type loop draws all the versions of "icons" at their screen-position - relative to the PLAYER coordinates
;in the current animation frame. It also checks for the non-transparent areas of the ICON image touching the non-
;transparent areas of the PLAYER image and updates the PLAYER's relavent energy-level values.
For icon.icons=Each icons
;this line draws the relavant icon image at coordinates "x" and "y" on (or off) the screen
;the correct icon image is chosen using the "icons"'s style as it's array position
;the x and y coordinates are worked out by subtracting the PLAYER's current position from the "icons"'s current position
;notice that we're using the same style of "SCREEN_WIDTH\2 - Half the ImageWidth of the Image" idea from the
;now we'll do the same for all the bullets on (or off) the screen
For i = 0 To bulletnum-1 ;this cycles though the array from position 1 to the final bullet number in the array..
;we use "bulletnum - 1" because there are (for example) 600 possible bullets that are stored
;in an array as 0, 1, 2, 3 ... 596, 597, 598 and 599
Select bullets(i,4) ;whose bullet is it? remember when we originally DIMmed this Array, I said that 0 was off, 1 was a PLAYER bullet and 2 was an enemy bullet?
Case 1
;if it's a player bullet then..
;draw this bullet at it's position, relative to the player, in it's current animation frame
;the following text will only be printed on the screen while the "FLAG_DEBUG" variable is set to 1 (F1, as I programmed it, on the main menu!)
If FLAG_DEBUG = 1 Then
;the TEXT command writes a STRING of text onto the screen at the given coordinates in the currently set font
;as I haven't used the LoadFont or SetFont commands, this will just be Blitz' default font.
Color 255,255,255 ;just in case, set the colo(u)r of the text to WHITE (255, 255, 255)
Text 0,0,"PLAYER ANGLE = " + PLAYER_ANGLE ;"Write the string 'PLAYER ANGLE = ' followed by the number held in PLAYER_ANGLE"
Text 0,20,"PLAYER SPEED = " + PLAYER_SPEED
Text 0,40,PLAYER_ANGLE + " / 5 = " + PLAYER_ANGLE/5
Text 0,60,"SHIPFRAME = " + game_player_frame
Text 0,80,"CO-ORDS = " + Int(PLAYER_X)
Text 140,80," , "
Text 160,80,Int(PLAYER_Y)
Text 0,120, "SCORE = " + PLAYER_SCORE
;this debug code counts how many bullets are "active" in the array (their 'style' - bullets(i,4) is not 0
;this was useful for deciding how big the array needed to be for the game, i.e.: how many bullets array
;containers would be in use at any one time.
;in reality, during the game, this value rarely gets above 100 active bullets,
;but I kept the "available" bullets (bulletnum) at 500 anyway.
;if you needed better performance, then decreasing the value of "bulletnum" might increase the speed of the program!
bulletcount = 0
For i = 0 To bulletnum-1
If bullets(i,4) > 0 Then
bulletcount = bulletcount + 1
End If
Next
Text 0,160, "BULLETS = " + bulletcount
End If
;just like in "menu_draw_update()", the user can press a key which just makes the value of "FLAG_SCREENSAVE" equal 1.
;when the code gets to here, it saves out the named Buffer as a BMP picture.
If FLAG_SAVESCREEN=1 Then
SaveBuffer (BackBuffer(),"CRGameScreen.bmp")
FLAG_SAVESCREEN = 0
End If
Flip ;as before in "menu_draw_update()", FLIP everything we've just drawn on the backbuffer and show it on the frontbuffer, i.e.: your monitor!!
End Function
;a little function which randomizes the x and y coordinates of each version of the "stars" Type, plus it's "depth" property
Function game_stars_randomize()
For star.stars=Each stars
star\x=Rnd(-5,SCREEN_WIDTH) ;create random x and y positions for all the stars
star\y=Rnd(-5,SCREEN_HEIGHT)
star\depth=Rnd(1,5) ;the depth of the stars results in our sexy parallex effect
Next
End Function
;an even littler function which just gives us random x and y coordinates for the player.
Function game_player_randomize()
PLAYER_X = Rand(0,GAME_AREA_X)
PLAYER_Y = Rand(0,GAME_AREA_Y)
End Function
;This is the last function and it's MARVELLOUS! :)
;This is the type of function that you should proactively *try* to write, because they're incredibly simple, yet incredibly effective
;and they impress the girls, I can tell you... Um.. No.. That's a lie..
;anyway the beauty of this function is that when you call it in the code above, you "feed" it some parameters.
;this one is expecting an X Coordinate, a Y Coordinate, an Angle value, a Speed value and a Style value.
;it doesn't care who or what they refer to, it just knows that it wants 5 numbers to work with.
;So, when a player requires two bullets to be made, I just give it "PLAYER_X, PLAYER_Y, PLAYER_ANGLE,
;PLAYER_SPEED and 1 (which just makes sure the correct bullet AnimImage is used!)
;When an enemy needs a bullet, I just give it "Enemy X, Enemy Y, Enemy Angle, Enemy Speed, 2"
;the createbullet() function does the rest and "produces" 2 bullets on the screen, starting at the end of the calling ship's laser.. BUT THAT'S ALL..
;After the bullet has been created, its "life" is calculated in another part of the code (it's x and y coordinates are altered based on the original angle and speed
;calculated in this function..) Enjoy
Function createbullet(x,y,angle,speed,style) ;creates 2 bullets starting on the end of a ship's lasers.
;there are "bulletnum" containers available in the "bullets()" array.
;if we've reached the maximum number of bullets, then the very first
;'bullet' will be overwritten with this new bullet.
;We do this by wrapping "nextbullet" back to 0, when it reaches bulletnum
If nextbullet = bulletnum-1 Then
nextbullet = 0
End If
;we want to create a bullet for the RIGHT laser of a ship
;by messing with the angleoffset value during debugging, I was able to come up with the
;correct values that looked right on screen (30 for this angle and 28 for the next calculation)
angleoffset = angle - 30
bullets(nextbullet,0) = x + 28*Cos(angleoffset) ;this sets the x and y pixel offset from the ship's game coordinates
bullets(nextbullet,1) = y + 28*Sin(angleoffset) ;in this case 28*Cos/Sin of the angle offset
;these next bits are just setting the origin of the bullets, including it's start position, speed and angle
;this is so the bullets can fly independently of the PLAYER's ship's Angle and speed (unlike everything else in the game)
bullets(nextbullet,2) = angle
If speed <= 0 Then ;this bit essentially stops the player catching up to his bullets, or bullets flying backwards
bullets(nextbullet,3) = 25 ;if the ship is still, or moving backwards, then a bullet fires off at a speed of 25
Else
bullets(nextbullet,3) = speed + 25 ;otherwise, if a ship is moving forwards, then the bullet flys off at a speed of 25, PLUS the ship's speed
End If
bullets(nextbullet,4) = style ;used to decide which bullet AnimImage should be used
bullets(nextbullet,5) = 0 ;used to keep track of the frame of animation a bullet is on
bullets(nextbullet,6) = bullets(nextbullet,0) ;used to know where a bullet began it's life in x and y coordinates
bullets(nextbullet,7) = bullets(nextbullet,1)
nextbullet = nextbullet+1 ;we've successfully created a bullet (whose 'life' from now on will be calculated back
;in "game_loop_update()", so now we move the "nextbullet" value on by 1
;ready for creation of the next bullet!
If nextbullet = bulletnum-1 Then ;exactly the same process, but with a 120 degree offset for the left hand laser